package com.laosg.java.asm.demo.test;


import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * <p>description: some thing </p>
 *
 * @author kevin
 * @date 9/5/21
 */
public class HelloAsm extends ClassLoader {

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        byte[] generate = generate();
        outputClazz(generate);
        Class<?> clazz = new HelloAsm().defineClass(
                "com.laosg.java.asm.demo.test.AsmHelloWorld",
                generate,
                0,
                generate.length
        );
        Method main = clazz.getMethod("main", String[].class);
        main.invoke(null,new Object[]{new String[]{}});
    }


    public static byte[] generate() {
        //定义类的生成classwriter
        ClassWriter classWriter = new ClassWriter(0);
        // 定义对象头，版本号，修饰符，全类名，签名，父类，实现的接口
        classWriter.visit(
                Opcodes.V1_8,
                Opcodes.ACC_PUBLIC,
                "com/laosg/java/asm/demo/test/AsmHelloWorld",
                null,
                "java/lang/Object",
                null
        );
        //添加方法，修饰符 方法名 描述符 签名 异常
        MethodVisitor methodVisitor = classWriter.visitMethod(
                Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC,
                "main",
                "([Ljava/lang/String;)V",
                null,
                null
        );

        //执行指令 获取静态属性
        methodVisitor.visitFieldInsn(
                Opcodes.GETSTATIC,
                "java/lang/System",
                "out",
                "Ljava/io/PrintStream;"
        );
        //加载常量
        methodVisitor.visitLdcInsn("hello xxxxx asm world");

        //调用方法
        methodVisitor.visitMethodInsn(
                Opcodes.INVOKEVIRTUAL,
                "java/io/PrintStream",
                "println",
                "(Ljava/lang/String;)V",
                false
        );
        //返回
        methodVisitor.visitInsn(Opcodes.RETURN);


        //设置操作数 栈的深度 和局部变量的大小
        methodVisitor.visitMaxs(2,1);

//        方法的结束
        methodVisitor.visitEnd();
        //类完成
        classWriter.visitEnd();

        return classWriter.toByteArray();
    }

    private static void outputClazz(byte[] bytes) {
        String pathName = HelloAsm.class.getResource("/").getPath() + "AsmHelloWorld.class";
        try (FileOutputStream fileOutputStream = new FileOutputStream(new File(pathName))) {
            System.out.println("ASM 类输出路径："+pathName);
            fileOutputStream.write(bytes);
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}
